package com.spc.car.web.log;

import com.spc.car.common.utils.ServletUtils;
import com.spc.car.common.utils.text.gson.GsonUtils;
import com.spc.car.core.framework.security.LoginUser;
import com.spc.car.core.framework.web.domain.AjaxResult;
import com.spc.car.data.entity.log.LogProjectOperation;
import com.spc.car.data.service.log.LogProjectOperationService;
import com.spc.car.web.log.annotation.LogProjectAction;
import com.spc.car.web.log.module.BaseLogModule;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Aspect
@Component
public class LogProjectOperationInterceptor extends BaseControllerInterceptor{

    @Autowired
    private LogProjectOperationService logProjectOperationService;

    @Around("execution (* *(..)) && @annotation(logProjectAction)")
    public Object around(final ProceedingJoinPoint joinPoint, final LogProjectAction logProjectAction)
            throws Throwable {

        Signature signature = joinPoint.getSignature();
        if (!(signature instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }

        Object result = null;
        Exception exception = null;
        Date startTime = new Date();
        try {
            result = joinPoint.proceed();
        } catch (Exception e) {
            log.error("项目信息操作失败: {}", e.getMessage(), e);
            exception = e;
        }

        Date endTime = new Date();

        // record
        recordOperation(joinPoint, logProjectAction, result, exception);
        // throw
        if (null != exception) {
            throw exception;
        }

        return result;
    }

    private void recordOperation(final ProceedingJoinPoint joinPoint,
                                 final LogProjectAction projectAction,
                                 final Object result, final Exception ex) {
        try {
            Method currentMethod = getCurrentMethod(joinPoint);
            // 获得方法中参数的注解
            //Annotation[][] annotations = currentMethod.getParameterAnnotations();
            //Parameter[] parameters = currentMethod.getParameters();
            //Object[] args = joinPoint.getArgs();

            Map<String, Object> paramsMap = convertArgsToMap(joinPoint, currentMethod);

            if (isSuccess(ex, result)) {

                LoginUser user = getCurrentPrincipal();
                if (Objects.nonNull(user)
                        && ArrayUtils.isNotEmpty(projectAction.action())) {
                    LogProjectOperation logProjectOperation = toOperationRecord(projectAction,
                            paramsMap, result);

                    logProjectOperation.setOperator(user.getUser().getNickName());
                    logProjectOperation.setOperatorAcc(user.getUser().getUserName());

                    logProjectOperation.setOperation(ServletUtils.getRequest().getRequestURI());
                    logProjectOperation.setParams(GsonUtils.toJson(paramsMap));
                    logProjectOperation.setRemark("[" + user.getUser().getNickName() + "]"
                            + logProjectOperation.getRemark());

                    logProjectOperationService.saveAsync(logProjectOperation);
                    log.info("log operate: {}", GsonUtils.toJson(logProjectOperation, true));
                }
            }

            //
            log.info("operate: {}", GsonUtils.toJson(paramsMap, true));

        } catch (Exception e) {
            log.error("记录操作失败: {}", e.getMessage(), e);
        }
    }

    private boolean isSuccess(Exception exception, Object result) {
        boolean success = false;
        if (Objects.isNull(exception)) {
            if (result instanceof AjaxResult) {
                 success = ((AjaxResult)result).isSuccess();
            } else {
                success = true;
            }
        }
        return success;
    }

    private LogProjectOperation toOperationRecord(LogProjectAction logProjectAction,
                                                  Map<String, Object> paramsMap,
                                                  Object result) throws Exception {
        ProjectAction[] projectActions = logProjectAction.action();
        if (ArrayUtils.isEmpty(projectActions)) {
            return null;
        }
        BaseLogModule<LogProjectOperation, LogProjectAction> baseLogModule =
                (BaseLogModule<LogProjectOperation, LogProjectAction>) logProjectAction.clz().newInstance();

        return baseLogModule.toLogRecord(logProjectAction, paramsMap, (AjaxResult) result);
    }

    public String escapeExprSpecialWord(String keyword) {
        if (StringUtils.isNotBlank(keyword)) {
            String[] fbsArr = { "\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "}", "|" };
            for (String key : fbsArr) {
                if (keyword.contains(key)) {
                    keyword = keyword.replace(key, "\\" + key);
                }
            }
        }
        return keyword;
    }

    /**
     * 获取当前用户
     * @return
     */
    public Authentication getCurrentUserAuthentication(){
        return SecurityContextHolder.getContext().getAuthentication();
    }

    /**
     * 获取当前用户
     * @return
     */
    public LoginUser getCurrentPrincipal() {
        Authentication authentication = getCurrentUserAuthentication();
        if (null == authentication) {
            return  null;
        }
        return null == authentication.getPrincipal()
                ? null : (LoginUser) authentication.getPrincipal();
    }
}
